package com.segersten.othello.service.akka.game;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.common.collect.Lists;
import com.segersten.othello.model.Board;
import com.segersten.othello.model.Color;
import com.segersten.othello.model.DefaultBoard;
import com.segersten.othello.model.IllegalActionException;
import com.segersten.othello.model.RoundAction;
import com.segersten.othello.service.akka.Actor;
import com.segersten.othello.service.akka.Auth;
import com.segersten.othello.service.akka.GameObjectPool;
import com.segersten.othello.service.akka.GameObjectReference;
import com.segersten.othello.service.akka.Player;
import com.segersten.othello.service.akka.game.dao.GameDao;
import com.segersten.othello.service.akka.lobby.BoardReference;
import com.segersten.othello.service.akka.lobby.GameFinishedAction;
import com.segersten.othello.service.akka.lobby.LobbyServiceActor;

public class OthelloBoardServiceActor implements Actor, Serializable {

	private static final Random random = new Random();
    private Board board;
	private Integer playerBlackPoints;
	private Integer playerWhitePoints;
	private String reference;
	private static Logger logger = Logger.getLogger(OthelloBoardServiceActor.class.getSimpleName());
	
	public OthelloBoardServiceActor(String reference) {
		this.reference = reference;
	}
	
    @Override
	public Object onReceive(GameObjectPool objectPool, GameDao game, Object message) throws Exception {
	    if (logger.isLoggable(Level.FINE)) {
	        logger.fine("OthelloBoardServiceActor.onReceive : " + message);
	    }
	    
		if (message instanceof CreateBoard) {
			if (board != null) {
				throw new IllegalStateException(String.format("Board has already been created for this actor %s",
						message));
			}
			CreateBoard createAction = (CreateBoard) message;
			createBoard(game, createAction);
			return state();
		} else if (message instanceof AuthenticatedAction) {
			AuthenticatedAction moveAction = (AuthenticatedAction) message;
			BoardActionResponse res = action(game, moveAction);
			updateScore(objectPool, game);
			return res;
		} else if (message instanceof State) {
		    return state();
		} else if (message instanceof AuthenticatedGiveUp) {
			AuthenticatedGiveUp canelAction = (AuthenticatedGiveUp) message;
			return cancel(objectPool, game, canelAction);
		} else if (message instanceof SessionLogginAction) {
		    SessionLogginAction loginAction = (SessionLogginAction) message;
		    return loginAction(game, loginAction);
		} else {
			throw new IllegalArgumentException("Unknown message: " + message);
		}
	}

	private Object loginAction(GameDao gameDao, SessionLogginAction loginAction)
    {
        Auth replacedSession = loginAction.getReplacedSession();
        Player player = loginAction.getPlayer();
        gameDao.updateSession(replacedSession, player);
        return null;
    }

    private Object updateScore(GameObjectPool gameObjectPool, GameDao gameDao) {
		if (playerBlackPoints == null && board.getNextColor() == null) {
			playerBlackPoints = board.getColorCount(Color.BLACK);
			playerWhitePoints = board.getColorCount(Color.WHITE);

			notifyLobbyGameFinished(gameObjectPool, gameDao);
		}
		return null;
	}

	private void notifyLobbyGameFinished(GameObjectPool gameObjectPool, GameDao gameDao) {
	    //logger.severe("notifyLobbyGameFinished " + getReferenceInteranl());
	    GameObjectReference lobbyActor = gameObjectPool.actorFor(LobbyServiceActor.SERVICE_NAME);
		assertNotNull(playerBlackPoints, "playerBlackPoints");
		assertNotNull(playerWhitePoints, "playerWhitePoints");
		assertNotNull(lobbyActor, "lobbyActor");
		gameObjectPool.tell(lobbyActor, new GameFinishedAction(new BoardReference(getReferenceInteranl()), gameDao.getPlayerBlackAuth(),
				playerBlackPoints, gameDao.getPlayerWhiteAuth(), playerWhitePoints));
	}

	private String getReferenceInteranl() {
		return reference;
	}

	private void assertNotNull(Object obj, String msg) {
		if (obj == null) {
			throw new RuntimeException("Was null " + msg);
		}
	}

	private BoardActionResponse cancel(GameObjectPool gameObjectPool, GameDao gameDao, AuthenticatedGiveUp giveUpAction) {
		if (gameDao.isAuthorized(giveUpAction.getAuth())) {
			return BoardActionResponse.NonAuth;
		}

		if (giveUpAction.getAuth().equals(gameDao.getPlayerBlackAuth())) {
			this.playerBlackPoints = -64;
			this.playerWhitePoints = 0;
		} else if (giveUpAction.getAuth().equals(gameDao.getPlayerWhiteAuth())) {
			this.playerBlackPoints = 0;
			this.playerWhitePoints = -64;
		} else {
			return BoardActionResponse.Unexpexted_error;
		}

		board.stopGame();
		notifyLobbyGameFinished(gameObjectPool, gameDao);
		return BoardActionResponse.OK;
	}

	private Object state() {
		return tellState();
	}

	private BoardActionResponse action(GameDao gameDao, AuthenticatedAction authAction) {

		if (board.getNextColor() == null || authAction.getAction().getColor().equals(board.getNextColor()) == false) {
			return BoardActionResponse.NotYourTurn;
		}
		if (gameDao.isAuthorized(authAction.getAuth())) {
			return BoardActionResponse.NonAuth;
		}
		try {
			Action moveAction = authAction.getAction();
			RoundAction action = moveAction.getPoint() == null ? RoundAction.createPass(moveAction.getColor())
					: RoundAction.createMove(moveAction.getPoint(), moveAction.getColor());
			makeMove(action);
		} catch (IllegalActionException e) {
		    return BoardActionResponse.NotYourTurn;
		}
		return BoardActionResponse.OK;
	}

	private void createBoard(GameDao gameDao, CreateBoard createAction) {
		List<Player> players = Arrays.asList(createAction.getPlayer1(), createAction.getPlayer2());
		int index = random.nextInt(2);
		Player player = players.get(index);
		gameDao.setPlayerBlack(player);
		player = players.get((index + 1) % 2);
		gameDao.setPlayerWhite(player);
		board = DefaultBoard.createDefaultBoard(createAction.getSize());
	}

	private void makeMove(RoundAction action) throws IllegalActionException {
		board.putBrick(action);
	}

	private Object tellState() {
		//System.out.println(board);
		BoardState2 stateResponse = new BoardState2(playerBlackPoints == null ? 0 : playerBlackPoints,
				playerWhitePoints == null ? 0 : playerWhitePoints, board.getRoundActions().size(),
				board.getNextColor(), Lists.transform(board.getRoundActions(), RoundActionToActionFunction.INSTANCE),
				board.getAvailblePoints(board.getNextColor()));
		return stateResponse;
	}

}
